package cn.stimd.spring.format.support;

import cn.stimd.spring.format.Formatter;
import cn.stimd.spring.format.FormatterRegistry;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Locale;
import java.util.Set;

/**
 * 用有格式化功能的转换服务
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class FormattingConversionService extends GenericConversionService implements FormatterRegistry {

    @Override
    public void addFormatter(Formatter<?> formatter) {
        Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
        addConverter(new PrinterConverter(fieldType, formatter));
        addConverter(new ParserConverter(fieldType, formatter));
    }


    private static class PrinterConverter implements GenericConverter {
        private final Class<?> fieldType;
        private final Formatter formatter;


        public PrinterConverter(Class<?> fieldType, Formatter<?> formatter) {
            this.fieldType = fieldType;
            this.formatter = formatter;
        }

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            if (source == null) {
                return "";
            }
            return this.formatter.print(source, Locale.getDefault());
        }
    }


    private static class ParserConverter implements GenericConverter {
        private final Class<?> fieldType;
        private final Formatter formatter;

        public ParserConverter(Class<?> fieldType, Formatter<?> formatter) {
            this.fieldType = fieldType;
            this.formatter = formatter;
        }

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(new ConvertiblePair(String.class, this.fieldType));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            String text = (String) source;
            if (!StringUtils.hasText(text)) {
                return null;
            }

            Object result;
            try {
                result = this.formatter.parse(text, Locale.getDefault());
            } catch (IllegalArgumentException ex) {
                throw ex;
            } catch (Throwable ex) {
                throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex);
            }

            if (result == null) {
                throw new IllegalStateException("Parsers are not allowed to return null: " + this.formatter);
            }
            return result;
        }
    }
}